//
//  SymbolParser.m
//  LinkMap
//
//  Created by aron on 2022/5/7.
//  Copyright © 2022 ND. All rights reserved.
//

#import "SymbolParser.h"
#import "ObjectResult.h"
#import "SymbolUtils.h"

typedef NS_ENUM(NSUInteger, LinkMapStep) {
    LinkMapStepNone = 0,
    LinkMapStepPath = 1,
    LinkMapStepObjectFiles,
    LinkMapStepSections,
    LinkMapStepSymbols,
    LinkMapStepDeadStrippedSymbols,
};

@interface SymbolParser ()
@property (nonatomic, copy) NSString *binaryPath;
@end

@implementation SymbolParser

- (void)startParseWithLinkMapFile:(NSURL *)linkMapFileUrl completion:(void(^)(BOOL success, NSError *error, LinkMapParseResult *result))completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString *content = [NSString stringWithContentsOfURL:linkMapFileUrl encoding:NSMacOSRomanStringEncoding error:nil];
        
        if (![self checkContent:content]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSError *error = [NSError errorWithDomain:@"" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Link Map文件格式有误"}];
                completion(NO, error, nil);
            });
            return ;
        }
        
        NSDictionary *symbolMap = [self symbolMapFromContent:content];
        NSArray <ObjectResult *>*symbols = [symbolMap allValues];
        NSArray *sortedSymbols = [SymbolUtils sortSymbols:symbols];
        
        LinkMapParseResult *result = [LinkMapParseResult new];
        result.objectResults = sortedSymbols;
        result.binaryPath = _binaryPath;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(YES, nil, result);
        });
    });
}

- (BOOL)checkContent:(NSString *)content {
    NSRange objsFileTagRange = [content rangeOfString:@"# Object files:"];
    if (objsFileTagRange.length == 0) {
        return NO;
    }
    NSString *subObjsFileSymbolStr = [content substringFromIndex:objsFileTagRange.location + objsFileTagRange.length];
    NSRange symbolsRange = [subObjsFileSymbolStr rangeOfString:@"# Symbols:"];
    if ([content rangeOfString:@"# Path:"].length <= 0||objsFileTagRange.location == NSNotFound||symbolsRange.location == NSNotFound) {
        return NO;
    }
    return YES;
}


- (NSMutableDictionary *)symbolMapFromContent:(NSString *)content {
    NSMutableDictionary <NSString *,ObjectResult *>*symbolMap = [NSMutableDictionary new];
    // 符号文件列表
    NSArray *lines = [content componentsSeparatedByString:@"\n"];
    
//    BOOL reachFiles = NO;
//    BOOL reachSymbols = NO;
//    BOOL reachSections = NO;
    LinkMapStep step = LinkMapStepNone;
    
    for(NSString *line in lines) {
        if([line hasPrefix:@"#"]) {
            if([line hasPrefix:@"# Path:"]) {
                step = LinkMapStepPath;
                NSArray *comps = [line componentsSeparatedByString:@":"];
                if (comps.count >= 2) {
                    NSString *binPath = comps[1];
                    _binaryPath = [binPath stringByReplacingOccurrencesOfString:@" " withString:@""];
                }
            }
            if([line hasPrefix:@"# Object files:"])
                step = LinkMapStepObjectFiles;
            else if ([line hasPrefix:@"# Sections:"])
                step = LinkMapStepSections;
            else if ([line hasPrefix:@"# Symbols:"])
                step = LinkMapStepSymbols;
            else if ([line hasPrefix:@"# Dead Stripped Symbols:"])
                step = LinkMapStepDeadStrippedSymbols;
        } else {
            if(step == LinkMapStepObjectFiles) {
                NSRange range = [line rangeOfString:@"]"];
                if(range.location != NSNotFound) {
                    ObjectResult *symbol = [ObjectResult new];
                    symbol.file = [line substringFromIndex:range.location+1];
                    NSString *key = [line substringToIndex:range.location+1];
                    symbolMap[key] = symbol;
                }
            } else if (step == LinkMapStepSymbols) {
                NSArray <NSString *>*symbolsArray = [line componentsSeparatedByString:@"\t"];
                if(symbolsArray.count == 3) {
                    NSString *fileKeyAndName = symbolsArray[2];
                    NSUInteger size = strtoul([symbolsArray[1] UTF8String], nil, 16);
                    
                    NSRange range = [fileKeyAndName rangeOfString:@"]"];
                    if(range.location != NSNotFound) {
                        NSString *key = [fileKeyAndName substringToIndex:range.location+1];
                        ObjectResult *symbol = symbolMap[key];
                        if(symbol) {
                            symbol.size += size;
                        }
                    }
                }
            }
        }
    }
    return symbolMap;
}

- (NSArray *)moduleParseResult {
    return nil;
}

@end
